﻿// Copyright © 2011 Vladislav Borovikov
// http://vividkit.codeplex.com/

namespace VividKit.Controls
{
	using System;
	using System.Windows;
	using System.Windows.Data;
	using System.Windows.Input;
	using Microsoft.Phone.Shell;
	using VividKit.PresentationModel;

	public abstract class PageBarItem : FrameworkElement
	{
		private readonly IApplicationBarMenuItem applicationBarItem;
		private bool enabledIsOverriden;
		private bool iconIsOverriden;
		private bool ignorePropertyChanged;
		private bool textIsOverriden;

		protected PageBarItem(IApplicationBarMenuItem item)
		{
			this.applicationBarItem = item;
			this.applicationBarItem.Click += HandleApplicationBarItemClick;
		}

		internal PageBar Owner { get; set; }

		protected IApplicationBarMenuItem ApplicationBarItem { get { return this.applicationBarItem; } }

		#region IsEnabled (DependencyProperty)

		public static readonly DependencyProperty IsEnabledProperty =
			DependencyProperty.Register("IsEnabled", typeof(bool), typeof(PageBarItem),
			new PropertyMetadata(true, new PropertyChangedCallback(HandleIsEnabledChanged)));

		public bool IsEnabled
		{
			get { return (bool)GetValue(IsEnabledProperty); }
			set { SetValue(IsEnabledProperty, value); }
		}

		protected virtual void OnIsEnabledChanged(DependencyPropertyChangedEventArgs e)
		{
			this.applicationBarItem.IsEnabled = e.NewValue != null ? (bool)e.NewValue : true;
		}

		private static void HandleIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var that = (PageBarItem)d;
			if (!that.enabledIsOverriden)
				that.enabledIsOverriden = !that.ignorePropertyChanged;
			that.OnIsEnabledChanged(e);
		}

		#endregion IsEnabled (DependencyProperty)

		#region IsVisible (DependencyProperty)

		public static readonly DependencyProperty IsVisibleProperty =
			DependencyProperty.Register("IsVisible", typeof(bool), typeof(PageBarItem),
			new PropertyMetadata(true, new PropertyChangedCallback(OnIsVisibleChanged)));

		public bool IsVisible
		{
			get { return (bool)GetValue(IsVisibleProperty); }
			set { SetValue(IsVisibleProperty, value); }
		}

		protected virtual void OnIsVisibleChanged(DependencyPropertyChangedEventArgs e)
		{
			if (this.Owner != null)
			{
				this.Owner.NotifyItemVisibilityChanged(this);
			}
		}

		private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((PageBarItem)d).OnIsVisibleChanged(e);
		}

		#endregion IsVisible (DependencyProperty)

		#region Text (DependencyProperty)

		public static readonly DependencyProperty TextProperty =
			DependencyProperty.Register("Text", typeof(string), typeof(PageBarItem),
			new PropertyMetadata(null, new PropertyChangedCallback(OnTextChanged)));

		public string Text
		{
			get { return (string)GetValue(TextProperty); }
			set { SetValue(TextProperty, value); }
		}

		protected virtual void OnTextChanged(DependencyPropertyChangedEventArgs e)
		{
			this.applicationBarItem.Text = e.NewValue != null ? e.NewValue.ToString() : null;
		}

		private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var that = (PageBarItem)d;

			if (!that.textIsOverriden)
				that.textIsOverriden = !that.ignorePropertyChanged;

			that.OnTextChanged(e);
		}

		#endregion Text (DependencyProperty)

		#region Icon (DependencyProperty)

		public static readonly DependencyProperty IconProperty =
			DependencyProperty.Register("Icon", typeof(Uri), typeof(PageBarItem),
			new PropertyMetadata(null, new PropertyChangedCallback(OnIconChanged)));

		public Uri Icon
		{
			get { return (Uri)GetValue(IconProperty); }
			set { SetValue(IconProperty, value); }
		}

		protected virtual void OnIconChanged(DependencyPropertyChangedEventArgs e)
		{
		}

		private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var that = (PageBarItem)d;

			if (!that.iconIsOverriden)
				that.iconIsOverriden = !that.ignorePropertyChanged;

			that.OnIconChanged(e);
		}

		#endregion Icon (DependencyProperty)

		#region Command (DependencyProperty)

		public static readonly DependencyProperty CommandProperty =
			DependencyProperty.Register("Command", typeof(ICommand), typeof(PageBarItem),
			new PropertyMetadata(null, new PropertyChangedCallback(HandleCommandPropertyChanged)));

		public ICommand Command
		{
			get { return (ICommand)GetValue(CommandProperty); }
			set { SetValue(CommandProperty, value); }
		}

		protected virtual void OnCommandChanged(DependencyPropertyChangedEventArgs e)
		{
			var oldCommand = e.OldValue as ICommand;
			if (oldCommand != null)
			{
				oldCommand.CanExecuteChanged -= HandleCommandCanExecuteChanged;
			}

			var newCommand = e.NewValue as ICommand;
			if (newCommand != null)
			{
				newCommand.CanExecuteChanged += HandleCommandCanExecuteChanged;
				HandleCommandCanExecuteChanged(newCommand, EventArgs.Empty);
			}

			var command = e.NewValue as IViewModelCommand;
			if (command != null)
			{
				SetBinding(TextProperty, textIsOverriden, new Binding("Name") { Source = command });
				SetBinding(IconProperty, iconIsOverriden, new Binding("Image") { Source = command });
			}
		}

		private static void HandleCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((PageBarItem)d).OnCommandChanged(e);
		}

		private void HandleCommandCanExecuteChanged(object sender, EventArgs e)
		{
			if (this.enabledIsOverriden)
				return;

			try
			{
				this.ignorePropertyChanged = true;

				var command = this.Command;
				if (command != null)
				{
					var commandParameter = this.CommandParameter;
					this.IsEnabled = command.CanExecute(commandParameter);
				}
			}
			finally
			{
				this.ignorePropertyChanged = false;
			}
		}

		#endregion Command (DependencyProperty)

		#region CommandParameter (DependencyProperty)

		public static readonly DependencyProperty CommandParameterProperty =
			DependencyProperty.Register("CommandParameter", typeof(object), typeof(PageBarItem),
			new PropertyMetadata(null, new PropertyChangedCallback(HandleCommandParameterChanged)));

		public object CommandParameter
		{
			get { return (object)GetValue(CommandParameterProperty); }
			set { SetValue(CommandParameterProperty, value); }
		}

		protected virtual void OnCommandParameterChanged(DependencyPropertyChangedEventArgs e)
		{
		}

		private static void HandleCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((PageBarItem)d).OnCommandParameterChanged(e);
		}

		#endregion CommandParameter (DependencyProperty)

		#region Click (RoutedEvent)

		/// <summary>
		/// Occurs when the page bar item is clicked.
		/// </summary>
		public event RoutedEventHandler Click;

		private void RaiseClick()
		{
			if (Click != null)
			{
				Click(this, new RoutedEventArgs());
			}
		}

		#endregion Click (RoutedEvent)

		private void HandleApplicationBarItemClick(object sender, EventArgs e)
		{
			if (TryExecuteCommand() == false)
			{
				RaiseClick();
			}
		}

		private void SetBinding(DependencyProperty property, bool isOverriden, Binding binding)
		{
			if (isOverriden)
				return;

			try
			{
				this.ignorePropertyChanged = true;
				this.SetBinding(property, binding);
			}
			finally
			{
				this.ignorePropertyChanged = false;
			}
		}

		private bool TryExecuteCommand()
		{
			var command = this.Command;

			if (command != null)
			{
				var commandParameter = this.CommandParameter;
				return command.TryExecute(commandParameter);
			}

			return false;
		}
	}
}